package org.sean.framework.web.config;

import org.sean.framework.bean.BaseResult;
import org.sean.framework.code.StatusInfo;
import org.sean.framework.logging.Logger;
import org.sean.framework.web.converter.BaseResponseBodyConverter;
import org.sean.framework.web.interceptor.ApiInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * 响应处理器
 *
 * @author xielei
 */
@ControllerAdvice
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnProperty(prefix = "app.response.convert", name = "enable", havingValue = "true", matchIfMissing = true)
public class ResponseBodyAdviceHandler implements ResponseBodyAdvice<Object> {

    private static final Logger LOG = Logger.newInstance(ResponseBodyAdviceHandler.class);

    /**
     * ResponseBody 转换器
     */
    @Autowired
    public BaseResponseBodyConverter<?> responseBodyConverter;

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request, ServerHttpResponse response) {
        LOG.debug("selectedConverterType : " + selectedConverterType.getName());
        return responseBodyConverter.convertResponseBody(body, null);
    }

    /**
     * @param returnType    返回值
     * @param converterType 转换类型
     * @return 是否支持
     */
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        Class<?> dc = returnType.getExecutable().getDeclaringClass();
        if (ErrorController.class.isAssignableFrom(dc) || !ApiInterceptor.isExecuted()
                || StringHttpMessageConverter.class.isAssignableFrom(converterType)
                || dc.getPackage().getName().contains("swagger")
                || dc.getPackage().getName().contains("springdoc")) {
            return false;
        }
        // 异常处理过一次数据,无需再次处理
        boolean fromError = null != returnType.getMethodAnnotation(ExceptionHandler.class);
        return !fromError;
    }

    /**
     * 默认异常处理
     *
     * @param e 异常
     * @return 响应数据
     * @throws Throwable 异常
     */
    @ResponseBody
    @ExceptionHandler(value = Throwable.class)
    @ResponseStatus(HttpStatus.OK)
    public Object errorHandler(Throwable e) throws Throwable {
        BaseResult<Void> body = new BaseResult<>();
        StatusInfo statusInfo = ThrowableHandler.handle(e);
        if (statusInfo != null) {
            body.setState(statusInfo.getCode());
            body.setMessage(statusInfo.getMessage());
            body.setLevel(statusInfo.getLevel());
        } else {
            body.setState(HttpStatus.INTERNAL_SERVER_ERROR.value());
            body.setMessage("未知异常,请联系管理员");
            body.setException(e.getMessage());
            body.setLevel(StatusInfo.Level.ERROR.getValue());
        }
        return body;
    }

}
